package cn.iocoder.yudao.module.infra.service.ocr;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.ocr.vo.config.OcrConfigPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.ocr.vo.config.OcrConfigSaveReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.ocr.vo.config.OcrConfigSimpleRespVO;
import cn.iocoder.yudao.module.infra.convert.ocr.OcrConfigConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.ocr.OcrConfigDO;
import cn.iocoder.yudao.module.infra.dal.mysql.ocr.OcrConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.OCR_CONFIG_NOT_EXISTS;

/**
 * OCR Configuration Service Implementation
 *
 * @author YuDao
 */
@Service
@Validated
@Slf4j
public class OcrConfigServiceImpl implements OcrConfigService {

    @Resource
    private OcrConfigMapper ocrConfigMapper;
    
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    
    @Resource
    private OcrConfigCacheMonitor cacheMonitor;
    
    // OCR配置缓存key前缀
    private static final String OCR_CONFIG_CACHE_KEY = "ocr_config:type_status:";
    // 缓存过期时间（30分钟）
    private static final long CACHE_EXPIRE_SECONDS = 30 * 60;

    @Override
    public Long createOcrConfig(OcrConfigSaveReqVO createReqVO) {
        OcrConfigDO ocrConfig = OcrConfigConvert.INSTANCE.convert(createReqVO);
        ocrConfigMapper.insert(ocrConfig);
        clearOcrConfigCache(ocrConfig.getType(), ocrConfig.getStatus());
        return ocrConfig.getId();
    }

    @Override
    public void updateOcrConfig(OcrConfigSaveReqVO updateReqVO) {
        OcrConfigDO existingConfig = validateOcrConfigExists(updateReqVO.getId());
        OcrConfigDO updateObj = OcrConfigConvert.INSTANCE.convert(updateReqVO);
        ocrConfigMapper.updateById(updateObj);
        // 清理相关缓存
        clearOcrConfigCache(existingConfig.getType(), existingConfig.getStatus());
        if (!existingConfig.getType().equals(updateObj.getType()) || 
            !existingConfig.getStatus().equals(updateObj.getStatus())) {
            clearOcrConfigCache(updateObj.getType(), updateObj.getStatus());
        }
        clearAvailableConfigCache(existingConfig.getStatus());
        clearAvailableConfigCache(updateObj.getStatus());
    }

    @Override
    public void deleteOcrConfig(Long id) {
        OcrConfigDO config = validateOcrConfigExists(id);
        ocrConfigMapper.deleteById(id);
        clearOcrConfigCache(config.getType(), config.getStatus());
        clearAvailableConfigCache(config.getStatus());
    }

    @Override
    public void deleteOcrConfigList(List<Long> ids) {
        List<OcrConfigDO> configs = ocrConfigMapper.selectBatchIds(ids);
        ocrConfigMapper.deleteByIds(ids);
        // 清理缓存
        configs.forEach(config -> {
            clearOcrConfigCache(config.getType(), config.getStatus());
            clearAvailableConfigCache(config.getStatus());
        });
    }

    @Override
    public OcrConfigDO getOcrConfig(Long id) {
        return ocrConfigMapper.selectById(id);
    }

    @Override
    public PageResult<OcrConfigDO> getOcrConfigPage(OcrConfigPageReqVO pageReqVO) {
        return ocrConfigMapper.selectPage(pageReqVO);
    }

    @Override
    public OcrConfigDO getOcrConfigByTypeAndStatus(String type, Integer status) {
        // 先从缓存中获取
        String cacheKey = buildCacheKey(type, status);
        OcrConfigDO cachedConfig = getCachedConfig(cacheKey);
        if (cachedConfig != null) {
            log.debug("OCR配置缓存命中，type: {}, status: {}", type, status);
            return cachedConfig;
        }
        
        // 缓存未命中，从数据库查询
        log.debug("OCR配置缓存未命中，从数据库查询，type: {}, status: {}", type, status);
        OcrConfigDO config = ocrConfigMapper.selectByTypeAndStatus(type, status);
        
        // 将结果放入缓存（包括 null 结果以防止缓存穿透）
        setCachedConfig(cacheKey, config);
        
        return config;
    }
    
    @Override
    public OcrConfigDO getAvailableOcrConfig(Integer status) {
        // 先从缓存中获取
        String cacheKey = buildAvailableCacheKey(status);
        OcrConfigDO cachedConfig = getCachedConfig(cacheKey);
        if (cachedConfig != null) {
            log.debug("可用OCR配置缓存命中，status: {}", status);
            return cachedConfig;
        }
        
        // 缓存未命中，从数据库查询第一个可用配置
        log.debug("可用OCR配置缓存未命中，从数据库查询，status: {}", status);
        OcrConfigDO config = ocrConfigMapper.selectAvailableConfig(status);
        
        // 将结果放入缓存（包括 null 结果以防止缓存穿透）
        setCachedConfig(cacheKey, config);
        
        return config;
    }

    @Override
    public String testOcrConfig(Long id) {
        // Validate existence
        OcrConfigDO config = validateOcrConfigExists(id);
        // Test OCR configuration by checking API key validity
        if (config.getApiKey() == null || config.getSecretKey() == null) {
            return "API Key or Secret Key is missing";
        }
        return "OCR configuration test successful";
    }

    @Override
    public List<OcrConfigSimpleRespVO> getOcrConfigSimpleList() {
        List<OcrConfigDO> list = ocrConfigMapper.selectList();
        return OcrConfigConvert.INSTANCE.convertSimpleList(list);
    }

    @Override
    public Map<Long, String> getOcrConfigNameMap(Set<Long> configIds) {
        if (configIds == null || configIds.isEmpty()) {
            return Collections.emptyMap();
        }

        List<OcrConfigDO> configs = ocrConfigMapper.selectBatchIds(configIds);
        return configs.stream()
                .collect(Collectors.toMap(
                        OcrConfigDO::getId,
                        OcrConfigDO::getName,
                        (existing, replacement) -> existing));
    }

    private OcrConfigDO validateOcrConfigExists(Long id) {
        OcrConfigDO config = ocrConfigMapper.selectById(id);
        if (config == null) {
            throw exception(OCR_CONFIG_NOT_EXISTS);
        }
        return config;
    }
    
    /**
     * 构建缓存key
     */
    private String buildCacheKey(String type, Integer status) {
        return OCR_CONFIG_CACHE_KEY + type + ":" + status;
    }
    
    /**
     * 构建可用配置缓存key
     */
    private String buildAvailableCacheKey(Integer status) {
        return "ocr_config:available:" + status;
    }
    
    /**
     * 从缓存中获取配置
     */
    private OcrConfigDO getCachedConfig(String cacheKey) {
        try {
            OcrConfigDO result = (OcrConfigDO) redisTemplate.opsForValue().get(cacheKey);
            if (result != null) {
                cacheMonitor.recordCacheHit();
            } else {
                cacheMonitor.recordCacheMiss();
            }
            return result;
        } catch (Exception e) {
            log.warn("获取OCR配置缓存失败，cacheKey: {}, error: {}", cacheKey, e.getMessage());
            cacheMonitor.recordCacheMiss();
            return null;
        }
    }
    
    /**
     * 将配置放入缓存
     */
    private void setCachedConfig(String cacheKey, OcrConfigDO config) {
        try {
            redisTemplate.opsForValue().set(cacheKey, config, CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
            log.debug("缓存OCR配置成功，cacheKey: {}", cacheKey);
        } catch (Exception e) {
            log.warn("缓存OCR配置失败，cacheKey: {}, error: {}", cacheKey, e.getMessage());
        }
    }
    
    /**
     * 清理OCR配置缓存
     */
    private void clearOcrConfigCache(String type, Integer status) {
        if (type == null || status == null) return;
        deleteCacheKey(buildCacheKey(type, status));
    }
    
    /**
     * 清理可用配置缓存
     */
    private void clearAvailableConfigCache(Integer status) {
        if (status == null) return;
        deleteCacheKey(buildAvailableCacheKey(status));
    }
    
    /**
     * 删除缓存键
     */
    private void deleteCacheKey(String cacheKey) {
        try {
            redisTemplate.delete(cacheKey);
            log.debug("清理缓存成功，cacheKey: {}", cacheKey);
        } catch (Exception e) {
            log.warn("清理缓存失败，cacheKey: {}, error: {}", cacheKey, e.getMessage());
        }
    }

}